///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <Scene/ObjectLoaders/OBJObjectLoader.h>
#include <File/File.h>
#include <Scene/Object.h>
//#include <Shader/ShaderManager.h>
#include <Model/MeshManager.h>
#include <Model/VertexBuffer.h>
#include <Model/Mesh.h>
#include <Material/Material.h>
#include <Util.h>

//-----------------------------------------------------------------------------

OBJObjectLoader::OBJObjectLoader()
{
	RegisterLoader( this );
}

//-----------------------------------------------------------------------------

OBJObjectLoader::~OBJObjectLoader()
{
	UnregisterLoader( this );
}

//-----------------------------------------------------------------------------

bool	OBJObjectLoader::CanLoad(File* file)
{
	if( !file->IsOpen() )
	{
		return false;
	}

	file->Seek( 0 );

	//check for .obj extension...

	return true;
}

//-----------------------------------------------------------------------------

Object*	OBJObjectLoader::LoadObject(File* file)
{
	if( !Load( file ) )
	{
		return NULL;
	}

	Object* obj = MGL_NEW Object();
	ASSERT( obj );
	if( MeshManager::GetInstance()->IsMeshLoaded( file->GetFilename() ) )
	{
		MeshData* meshData = MeshManager::GetInstance()->GetMesh( file->GetFilename() );
		Mesh* mesh = MGL_NEW Mesh( file->GetFilename() );
		mesh->SetMeshData( meshData );
		obj->AddMesh( mesh );
	}
	else
	{
		DEBUG_ERR( "[OBJObjectLoader::LoadObject] Mesh not loaded '%s'!\n", file->GetFilename().c_str() );
	}

	return obj;
}

//-----------------------------------------------------------------------------

bool		OBJObjectLoader::Load(File* file)
{
	if( !CanLoad( file ) )
	{
		return false;
	}

	file->Seek( 0 );

	if( readData( file ) )
	{
		return processData( file );
	}

	return false;
}

//-----------------------------------------------------------------------------

bool		OBJObjectLoader::readData( File* file )
{
	std::string line;
	std::string word;

	vertices.clear();
	normals.clear();
	texcoord.clear();
	faces.clear();

	face_count = 0;
	norm_count = 0;
	tex_count = 0;
	vex_count = 0;

	while( !file->IsEOF() )
	{
		line = file->ReadLine();
		word = extractWord ( line );
		if ( word.empty () )
		{
			continue;
		}
		if ( word.at ( 0 ) == '#' )
		{
			continue;
		}
		else if ( word == "vt" )
		{
			texcoord [ ++tex_count ] = processTex ( line );
		}
		else if ( word == "vn" )
		{
			normals [ ++norm_count ] = processNorm ( line );
		}
		else if ( word == "v" )
		{
			vertices [ ++vex_count ] = processVertex ( line );
		}
		else if ( word == "f" )
		{
			faces [ ++face_count ] = processFace ( line );
		}
	}

	return true;
}

//-----------------------------------------------------------------------------

bool		OBJObjectLoader::processData( File* file )
{
	char p;
	u32 a;
	Vec3f v;
	Vec3f n;
	Vec2f t;
	std::map<u32,Vec3f>::iterator vit;
	std::map<u32,Vec3f>::iterator nit;
	std::map<u32,Vec2f>::iterator tit;
	std::map<u32,OBJFace>::iterator it;
	OBJFacePoint fp;

	u8 cosize = 0;
	VertexBuffer* vbp = MGL_NEW VertexBuffer();
	if( vertices.size() > 0 )
	{
		vbp->AddComponent( eVDT_POSITION, eVCT_FLOAT, sizeof( f32 ), 3 );
		cosize += 3;
	}
	else
	{
		ASSERT( false );
	}
	if( normals.size() > 0 )
	{
		vbp->AddComponent( eVDT_NORMAL, eVCT_FLOAT, sizeof( f32 ), 3 );
		cosize += 3;
	}
	if( texcoord.size() > 0 )
	{
		vbp->AddComponent( eVDT_TEXCOORD, eVCT_FLOAT, sizeof( f32 ), 2 );
		cosize += 2;
	}
	vbp->InitData( face_count * 3 );
	f32* vA = (f32*) vbp->GetDataPtr();
	f32* vAc = NULL;

	for ( it = faces.begin() ; it != faces.end(); it++ )
	{
		for ( p = 0; p < 3 ; p++ )
		{
			vAc = vA;
			if ( (size_t)p >= it->second.points.size () ) {
				DEBUG_LOG ( "WARNING: [MODEL] Face %d has not got %d vertices defined\n", it->first, p+1 );
				return false;
			}
			fp = it->second.points.at( p );
			a = fp.v;
			if ( a != 0 )
			{
				if ( ( vit = vertices.find ( a ) ) != vertices.end () ){
					v = vit->second;
					vAc [ 0 ] = v.getX();
					vAc [ 1 ] = v.getY();
					vAc [ 2 ] = v.getZ();
					vAc += 3;
				} else {
					DEBUG_LOG ( "ERROR: [MODEL] Can not find vertex with index %d defined in face %d\n", a, it->first );
				}
			} else {
				DEBUG_LOG ( "ERROR: [MODEL] Vertex index for face %d is 0\n", it->first );
			}
			a = fp.n;
			if ( a != 0 )
			{
				if ( ( nit = normals.find ( a ) ) != normals.end () ){
					n = nit->second;
					vAc [ 0 ] = n.getX();
					vAc [ 1 ] = n.getY();
					vAc [ 2 ] = n.getZ();
					vAc += 3;
				} else {
					DEBUG_LOG ( "ERROR: [MODEL] Can not find normal with index %d defined in face %d\n", a, it->first );
				}
			}
			a = fp.t;
			if ( a != 0 )
			{
				if ( ( tit = texcoord.find ( a ) ) != texcoord.end () ){
					t = tit->second;
					vAc [ 0 ] = t.getX();
					vAc [ 1 ] = t.getY();
					vAc += 2;
				} else {
					DEBUG_LOG ( "ERROR: [MODEL] Can not find texture coordonates with index %d defined in face %d\n", a, it->first );
				}
			}
			vA += cosize;
		}
	}

	MeshBuffers mb;
	mb.indexBuffer = NULL;
	mb.vertexBuffer = vbp;

	MeshData *mesh = MGL_NEW MeshData( file->GetFilename() );
	mesh->AddMeshBuffers( mb );

	vertices.clear();
	normals.clear();
	texcoord.clear();
	faces.clear();

	return true;
}

//-----------------------------------------------------------------------------

Vec3f		OBJObjectLoader::processVertex ( std::string& s )
{
	Vec3f v;
	v.setX ( (float)atof( extractWord ( s ).c_str () ) );
	v.setY ( (float)atof( extractWord ( s ).c_str () ) );
	v.setZ ( (float)atof( extractWord ( s ).c_str () ) );
	return v;
}

//-----------------------------------------------------------------------------

Vec3f		OBJObjectLoader::processNorm ( std::string& s )
{
	Vec3f v;
	v.setX ( (float)atof( extractWord ( s ).c_str () ) );
	v.setY ( (float)atof( extractWord ( s ).c_str () ) );
	v.setZ ( (float)atof( extractWord ( s ).c_str () ) );
	return v;
}

//-----------------------------------------------------------------------------

Vec2f		OBJObjectLoader::processTex ( std::string& s )
{
	Vec2f v;
	v.setX ( (float)atof( extractWord ( s ).c_str () ) );
	v.setY ( (float)atof( extractWord ( s ).c_str () ) );
	return v;
}

//-----------------------------------------------------------------------------

OBJFace		OBJObjectLoader::processFace ( std::string& s )
{
	OBJFace f;
	std::string word;

	f.points.clear ();
	word = extractWord ( s );
	while ( !word.empty() )
	{
		f.points.push_back ( processFacePoint ( word ) );
		word = extractWord ( s );
	}

	return f;
}

//-----------------------------------------------------------------------------

OBJFacePoint	OBJObjectLoader::processFacePoint ( std::string& str )
{
	OBJFacePoint f;
	size_t pos;

	pos = str.find_first_of("/");
	if( std::string::npos != pos )
	{
		f.v = atoi ( str.substr ( 0, pos ).c_str () );
		str.erase ( 0, pos + 1 );
	}else{
		f.v = atoi ( str.c_str () );
		f.t = 0;
		f.n = 0;
		return f;
	}

	pos = str.find_first_of("/");
	if( std::string::npos != pos )
	{
		if ( pos == 0 )
		{
			f.t = 0;
		}else{
			f.t = atoi ( str.substr ( 0, pos ).c_str () );
		}
		str.erase ( 0, pos + 1 );
	}else{
		f.t = atoi ( str.c_str () );
		f.n = 0;
		return f;
	}

	if ( str.empty() )
	{
		f.n = 0;
	}else{
		f.n = atoi ( str.c_str () );
	}

	return f;
}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
